home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
cdsend
/
cdsend.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
9KB
|
359 lines
/***********************************************************
Copyright 1991, 1992 by Stichting Mathematisch Centrum, Amsterdam, The
Netherlands.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the names of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
******************************************************************/
/* $Id: cdsend.c,v 1.2 1993/01/18 10:02:47 sjoerd Exp $ */
/*
* cdsend reads audio CD's over the SCSI bus, converts it to a format
* suitable for broadcast, and writes this converted data to standard
* output. Optionally, the msuic is also played over the system's own
* speakers.
*
* Options.
* -n use non-degradable priority for the program (works
* only if installed set-uid root). The default is to
* not change the priority.
* -d daemon mode. When a CD finishes, hang around until
* the next CD is put in the CD player and play it. The
* default is to exit after playing one CD.
* -p play the music over the system's speakers. Default is
* to not play the music.
* -l convert the data to 2-byte-per-sample linear format.
* Default is to convert to 1-byte-per-sample U-law.
* -c file use the specified file to write the CD's
* identification to. Default is to write to the file
* $HOME/.CDcatalognumber.
*/
#include "cdsend.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <signal.h>
#include <limits.h>
#include <sys/prctl.h>
#include <sys/schedctl.h>
#include <audio.h>
#include <fcntl.h>
static ALport port; /* audio output port */
static int playaudio; /* 1 iff we changed output rate */
static volatile int silent = 1; /* 1 iff we must be quiet */
static volatile int stop_playing; /* 1 if we must stop playing */
static int linear = 0; /* 1 iff we produce linear (2-byte) output */
static long oldparams[2] = {AL_OUTPUT_RATE, 0};
static long newparams[2] = {AL_OUTPUT_RATE, AL_RATE_44100};
static char *cdtoc, *cdid;
static char cdcatnamebuf[1024];
static char *cdcatname = cdcatnamebuf;
extern char *db_get_TOC(CDPLAYER *, CDSTATUS *);
extern char *db_get_id(CDPLAYER *, CDSTATUS *);
extern char *optarg;
/* newer libraries use CDaddcallback and have a backward compatibility */
/* define for CDsetcallback, we do it the other way round */
#ifndef CDsetcallback
#define CDaddcallback CDsetcallback
#endif
#define DEFCDCATNAME ".CDcatalognumber"
/*
* Called when the program is interrupted. This means that the audio
* params have to be reset, but only if we've changed them.
*/
static void
die(int sig)
{
if (playaudio)
ALsetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
exit(sig);
}
/*
* Called when we get a SIGHUP. This means that we must switch from
* playing the audio to being silent or vv.
*/
static void
toggle(void)
{
silent = !silent;
}
/*
* Called when we get a SIGUSR1. This means we have to stop reading
* the CD (and eject it). This is useful if the CD player is needed
* for official business.
*/
static void
eject(void)
{
stop_playing = 1;
}
/*
* Callback routine, called to do something to the audio data.
*/
static void
handleaudio(void *arg, CDDATATYPES type, short *audio)
{
if (!playaudio && !silent) {
/* we were silent but want to start playing, so set */
/* audio params */
ALgetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
ALsetparams(AL_DEFAULT_DEVICE, newparams, 2L);
playaudio = 1;
}
/* play the audio, but only if enough space in output port */
if (playaudio && ALgetfillable(port) >= CDDA_NUMSAMPLES)
ALwritesamps(port, audio, CDDA_NUMSAMPLES);
if (playaudio && silent) {
/* we were playing but want to be silent now, so reset */
/* audio params */
ALsetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
playaudio = 0;
}
/* actually do the work we're being paid for */
convert_audio_and_print(audio, CDDA_NUMSAMPLES / 2, linear);
}
/*
* Callback routine, called when the CD contains a catalog number.
* This routine writes the catalog number and the table of contents to
* a file. Other programs can use this information to display the
* title of the CD being played.
*/
static void
handlecatalog(void *arg, CDDATATYPES type, char *data)
{
int i;
FILE *fp;
for (i = 0; i < 13; i++)
if (data[i] != 0)
break;
if (i == 13) /* catalog # is null, so ignore it */
return;
if ((fp = fopen(cdcatname, "w")) == NULL)
return;
fprintf(fp, "hash=%s\n", cdid);
fprintf(fp, "toc=%s\n", cdtoc);
fprintf(fp, "catalog=");
for (i = 0; i < 13; i++)
fprintf(fp, "%c", *data++ + '0');
fprintf(fp, "\n");
fclose(fp);
}
/*
* This routine is called to write identification information in the
* .CDcatalognumber file. If there is a catalog number on the CD,
* this information is overwritten, but if there is no catalog number
* on the CD, the information written here can be used.
*/
static void
nocatalog(CDSTATUS *cdstatus)
{
FILE *fp;
if ((fp = fopen(cdcatname, "w")) == NULL)
return;
fprintf(fp, "hash=%s\n", cdid);
fprintf(fp, "toc=%s\n", cdtoc);
fprintf(fp, "tmsf=%d.%d.%d.%d\n", cdstatus->last, cdstatus->total_min,
cdstatus->total_sec, cdstatus->total_frame);
fclose(fp);
}
/*
* Wait for an audio CD to be inserted into the player.
*/
static void
waitforcd(void)
{
CDPLAYER *cdp;
CDSTATUS status;
for (;;) {
for (;;) {
if ((cdp = CDopen(0, 0)) != 0)
break;
/* opening the CD player failed; wait a while */
/* and try again */
sginap(60);
}
/* opening the CD player succeeded; now wait until the */
/* player is ready */
for (;;) {
if (!CDgetstatus(cdp, &status)) {
CDclose(cdp);
break;
}
if (status.state == CD_READY) {
if (!status.scsi_audio) {
fprintf(stderr, "cdsend: CD-ROM player does not support audio CD's\n");
CDclose(cdp);
exit(1);
}
CDclose(cdp);
return;
}
sginap(60);
}
sginap(60);
}
}
/*
* This routine reads one whole CD and when it is finished, it ejects
* the CD.
*/
static void
play(void)
{
CDPLAYER *cdp;
CDPARSER *parser;
CDFRAME buf[12];
CDSTATUS cdstatus;
FILE *fd;
int i, n, first = 1;
ALconfig c;
waitforcd();
stop_playing = 0;
cdp = CDopen(0, 0);
if (cdp == 0)
return;
CDgetstatus(cdp, &cdstatus);
if (cdtoc)
free(cdtoc);
if (cdid)
free(cdid);
cdtoc = db_get_TOC(cdp, &cdstatus);
cdid = db_get_id(cdp, &cdstatus);
nocatalog(&cdstatus);
if ((parser = CDcreateparser()) == 0) {
perror("CDcreateparser");
exit(1);
}
ALgetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
c = ALnewconfig();
ALsetwidth(c, AL_SAMPLE_16);
ALsetchannels(c, AL_STEREO);
port = ALopenport("cd", "w", c);
CDaddcallback(parser, cd_audio, (CDCALLBACKFUNC) handleaudio, 0);
CDaddcallback(parser, cd_catalog, (CDCALLBACKFUNC) handlecatalog, 0);
init_convert();
while (!stop_playing) {
n = CDreadda(cdp, buf, 12);
if (first && n == 0) {
/* apparantly not an audio CD */
CDclose(cdp);
ALcloseport(port);
ALfreeconfig(c);
close(creat(cdcatname, 0666));
sginap(300);
return;
}
first = 0;
if (n < 0) {
perror("CDreadda");
exit(1);
}
if (n == 0)
break;
for (i = 0; i < n && !stop_playing; i++)
CDparseframe(parser, &buf[i]);
}
if (playaudio) {
ALsetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
playaudio = 0;
}
CDeject(cdp);
CDdeleteparser(parser);
CDclose(cdp);
ALcloseport(port);
ALfreeconfig(c);
close(creat(cdcatname, 0666));
}
main(int argc, char **argv)
{
int c;
int daemon = 0;
sprintf(cdcatname, "%s/%s", getenv("HOME"), DEFCDCATNAME);
while ((c = getopt(argc, argv, "c:npdl")) != EOF) {
switch (c) {
case 'c':
cdcatname = optarg;
break;
case 'n':
schedctl(NDPRI, 0, NDPNORMMAX);
break;
case 'd':
daemon = 1;
break;
case 'p':
silent = 0;
break;
case 'l':
linear = 1;
break;
}
}
setuid(getuid());
if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
sigset(SIGINT, die);
sigset(SIGTERM, die);
sigset(SIGHUP, toggle);
sigset(SIGUSR1, eject);
if (daemon)
for (;;)
play();
else
play();
die(0);
}